#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
PDD Redis监听器 - 简化版
监听Redis任务队列，自动执行PDD爬取
"""

import redis
import json
import time
import requests
import logging
import pickle
import os
from faker import Faker

# 处理相对导入问题
try:
    from .scraper import PDDScraper
except ImportError:
    # 如果相对导入失败，尝试绝对导入
    import sys
    import os
    sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
    from PDD.scraper import PDDScraper

# 配置日志 - 修复编码问题
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler('pdd_monitor.log', encoding='utf-8'),
        logging.StreamHandler()
    ]
)
logger = logging.getLogger(__name__)


def get_random_ua():
    """获取随机User-Agent"""
    fake = Faker()
    return fake.user_agent()



# Redis配置
REDIS_CONFIG = {
    'host': '8.155.9.53',
    'port': 6300,
    'password': 'jhkdjhkjdhsIUTYURTU_XF6dAk',
    'task_key': ':1:自动获取产品列表'
}

# API配置
API_CONFIG = {
    'url': 'http://192.168.5.12:8000/api/v1/products/batch_add_processed_products/',
    'headers': {'Content-Type': 'application/json', 'User-Agent': get_random_ua()}
}

class PDDRedisMonitor:
    """PDD Redis任务监听器"""
    
    def __init__(self, redis_config=None, api_config=None):
        self.redis_config = redis_config or REDIS_CONFIG
        self.api_config = api_config or API_CONFIG
        
        # Redis连接
        self.redis_client = redis.Redis(
            host=self.redis_config['host'],
            port=self.redis_config['port'],
            password=self.redis_config['password'],
            decode_responses=False  # 获取原始字节数据
        )
        
        logger.info(f"PDD Redis监听器已初始化")
        logger.info(f"Redis: {self.redis_config['host']}:{self.redis_config['port']}")
        logger.info(f"API: {self.api_config['url']}")
    
    def get_redis_tasks(self):
        """获取Redis中的任务列表"""
        try:
            raw_data = self.redis_client.get(self.redis_config['task_key'])
            
            if not raw_data:
                return []
            
            # 智能解析数据（兼容pickle和JSON）
            try:
                data = pickle.loads(raw_data)
                if isinstance(data, str):
                    return json.loads(data)
                elif isinstance(data, list):
                    return data
                else:
                    return []
            except:
                try:
                    text_data = raw_data.decode('utf-8')
                    return json.loads(text_data)
                except:
                    logger.error("❌ 无法解析Redis数据格式")
                    return []
                    
        except Exception as e:
            logger.error(f"❌ 获取Redis任务失败: {e}")
            return []
    
    def update_redis_tasks(self, task_list):
        """更新Redis中的任务列表"""
        try:
            # 始终保存任务列表，即使为空，避免删除key导致其他监听器解析错误
            json_data = json.dumps(task_list, ensure_ascii=False)
            self.redis_client.set(self.redis_config['task_key'], json_data.encode('utf-8'))
            
            if task_list:
                logger.info(f"💾 已更新Redis，剩余任务: {len(task_list)}")
            else:
                logger.info("🗑️ 任务队列已清空（保留空列表）")
        except Exception as e:
            logger.error(f"❌ 更新Redis失败: {e}")
    
    def get_pdd_credentials_and_login_mode(self):
        """获取PDD登录凭证和登录方式"""
        # 先尝试环境变量
        username = os.getenv('PDD_USERNAME')
        password = os.getenv('PDD_PASSWORD')
        
        if username and password:
            logger.info("✅ 发现环境变量凭证")
            print(f"\n🔍 发现环境变量凭证: {username[:3]}***")
            use_env = input("是否使用环境变量凭证？(y/n, 默认y): ").strip().lower()
            if use_env in ['', 'y', 'yes']:
                return username, password, "auto"
        
        # 选择登录方式
        print("\n🔐 请选择登录方式:")
        print("  1. 自动登录（程序自动填写用户名密码）")
        print("  2. 手动登录（浏览器中手动操作，适合有验证码的情况）")
        
        while True:
            choice = input("请选择 (1/2): ").strip()
            if choice == '1':
                login_mode = "auto"
                break
            elif choice == '2':
                login_mode = "manual"
                break
            else:
                print("❌ 无效选择，请输入 1 或 2")
        
        if login_mode == "auto":
            # 自动登录需要用户名密码
            print("\n📝 请输入PDD登录凭证:")
            try:
                username = input("PDD用户名: ").strip()
                password = input("PDD密码: ").strip()
                
                if username and password:
                    return username, password, "auto"
                else:
                    logger.error("❌ 用户名或密码不能为空")
                    return None, None, None
                    
            except KeyboardInterrupt:
                logger.info("🛑 用户取消输入")
                return None, None, None
        else:
            # 手动登录不需要用户名密码
            return None, None, "manual"
    
    def manual_login_process(self, scraper):
        """手动登录流程"""
        try:
            logger.info("🌐 正在打开PDD登录页面...")
            # 导航到PDD登录页面
            scraper.driver.get("https://mms.pinduoduo.com/login")
            
            print("\n" + "="*60)
            print("🔐 手动登录模式")
            print("="*60)
            print("📋 请在浏览器中完成以下操作：")
            print("   1. 输入用户名和密码")
            print("   2. 完成验证码验证（如果有）")
            print("   3. 点击登录按钮")
            print("   4. 等待登录成功")
            print("   5. 登录成功后回到此窗口按 Enter 键继续")
            print("-" * 60)
            
            # 等待用户手动登录
            input("✋ 请在浏览器中完成登录，然后按 Enter 键继续...")
            
            # 验证登录状态
            logger.info("🔍 验证登录状态...")
            
            # 检查当前URL是否表示登录成功
            current_url = scraper.driver.current_url
            if "login" not in current_url.lower() and "mms.pinduoduo.com" in current_url:
                logger.info("✅ 登录验证成功")
                return True
            else:
                # 尝试其他方式验证登录状态
                try:
                    # 检查是否有登录用户的标识元素
                    import time
                    time.sleep(2)
                    
                    # 如果还在登录页面，说明登录失败
                    if "login" in scraper.driver.current_url.lower():
                        logger.warning("⚠️ 仍在登录页面，可能登录失败")
                        retry = input("是否重试登录？(y/n): ").strip().lower()
                        if retry in ['y', 'yes']:
                            return self.manual_login_process(scraper)
                        return False
                    else:
                        logger.info("✅ 已离开登录页面，假定登录成功")
                        return True
                        
                except Exception as e:
                    logger.warning(f"登录状态验证出错: {e}")
                    # 如果验证出错，询问用户
                    success = input("登录是否成功？(y/n): ").strip().lower()
                    return success in ['y', 'yes']
                    
        except Exception as e:
            logger.error(f"❌ 手动登录过程出错: {e}")
            return False
    
    def get_max_pages_setting(self):
        """获取分页抓取设置"""
        # 可以通过环境变量设置
        env_max_pages = os.getenv('PDD_MAX_PAGES')
        if env_max_pages:
            try:
                max_pages = int(env_max_pages)
                logger.info(f"✅ 使用环境变量分页设置: {max_pages}")
                return max_pages
            except ValueError:
                logger.warning(f"⚠️ 环境变量PDD_MAX_PAGES值无效: {env_max_pages}")
        
        # 默认设置：获取所有页（None）
        # 你可以在这里修改默认行为：
        # return 3  # 只抓取前3页
        # return None  # 抓取所有页
        return None
     
    def process_pdd_task(self, task):
        """处理单个PDD任务"""
        store_id = task.get("store_id")
        store_url = task.get("store_url")
        platform = task.get("platform", "").strip()
        
        # 获取重试次数
        retry_count = task.get("retry_count", 0)
        max_retries = 3  # 最大重试次数
        
        logger.info(f"🔍 处理PDD任务: {store_url} (store_id: {store_id}, 重试次数: {retry_count}/{max_retries})")
        
        # 如果重试次数超过限制，标记为永久失败
        if retry_count >= max_retries:
            logger.error(f"❌ 任务重试次数超过限制 ({max_retries})，标记为永久失败")
            return True  # 返回True以删除任务，避免无限重试
        
        if platform != "拼多多":
            logger.warning(f"❌ 平台不匹配: {platform}，跳过")
            return False
        
        # 获取登录凭证和登录方式
        username, password, login_mode = self.get_pdd_credentials_and_login_mode()
        if login_mode is None:
            logger.error("❌ 未选择登录方式或凭证获取失败")
            return False
        
        products = []
        success = False
        
        api_products = []
        
        try:
            # 使用PDD爬虫抓取数据 - 改为可见模式便于调试
            with PDDScraper(headless=False) as scraper:
                login_success = False
                
                if login_mode == "auto":
                    logger.info("🤖 开始自动登录PDD...")
                    login_success = scraper.login(username, password)
                elif login_mode == "manual":
                    logger.info("👤 开始手动登录模式...")
                    login_success = self.manual_login_process(scraper)
                
                if login_success:
                    logger.info("✅ PDD登录成功")
                    
                    logger.info("正在抓取商品数据...")
                    
                    # 先导航到商品列表页面（与pdd_scraper.py保持一致）
                    if scraper.navigate_to_goods_list():
                        logger.info("成功导航到商品列表页面")
                        
                        # 等待页面完全加载
                        import time
                        time.sleep(3)
                        
                        # 获取商品数据（支持多页抓取，设为None获取所有页）
                        max_pages = self.get_max_pages_setting()
                        logger.info(f"📄 分页设置: {max_pages if max_pages else '获取所有页'}")
                        products = scraper.get_all_products(max_pages=max_pages, get_details=True)
                    else:
                        logger.error("无法导航到商品列表页面")
                        products = []
                    
                    if products:
                        logger.info(f"成功获取 {len(products)} 个商品")
                        
                        # 在这里转换为API格式
                        logger.info("正在转换为API格式...")
                        api_products = scraper.convert_to_api_format(products)
                        logger.info(f"已转换为API格式，商品数量: {len(api_products)}")
                        success = True
                    else:
                        logger.warning("未获取到商品数据")
                        # 添加调试信息
                        try:
                            logger.info(f"调试信息 - 当前URL: {scraper.driver.current_url}")
                            logger.info(f"调试信息 - 页面标题: {scraper.driver.title}")
                            
                            # 检查页面是否有商品表格
                            from selenium.webdriver.common.by import By
                            table_elements = scraper.driver.find_elements(By.CSS_SELECTOR, "table.TB_tableWrapper_5-161-0")
                            logger.info(f"找到表格元素数量: {len(table_elements)}")
                            
                            product_rows = scraper.driver.find_elements(By.CSS_SELECTOR, "tbody tr.TB_tr_5-161-0")
                            logger.info(f"找到商品行数量: {len(product_rows)}")
                        except Exception as debug_e:
                            logger.error(f"调试检查失败: {debug_e}")
                else:
                    logger.error("PDD登录失败")
                    
        except Exception as e:
            logger.error(f"❌ PDD爬取异常: {e}")
        
        # 提交到API
        try:
            payload = {
                "store_id": store_id,
                "products": api_products,
                "status": success
            }
            
            logger.info(f"🚀 提交数据到API: store_id={store_id}, 商品数量={len(api_products)}, status={success}")
            
            response = requests.post(
                self.api_config['url'],
                json=payload,
                headers=self.api_config['headers'],
                timeout=60
            )
            
            if response.status_code == 200:
                logger.info(f"✅ API提交成功: {store_id}")
                return True
            else:
                logger.error(f"❌ API提交失败: {response.status_code} - {response.text}")
                return False
                
        except Exception as e:
            logger.error(f"❌ API请求异常: {e}")
            return False
    
    def show_task_management_menu(self, task_list, pdd_task, pdd_task_index):
        """显示任务管理菜单"""
        print("\n" + "="*60)
        print("📋 发现PDD任务，请选择操作:")
        print("="*60)
        print(f"📤 任务详情:")
        print(f"   🆔 Store ID: {pdd_task.get('store_id', 'N/A')}")
        print(f"   🌐 Store URL: {pdd_task.get('store_url', 'N/A')}")
        print(f"   🏷️  Platform: {pdd_task.get('platform', 'N/A')}")
        print(f"   🔄 重试次数: {pdd_task.get('retry_count', 0)}/3")
        print("-"*60)
        print("🎯 可用操作:")
        print("  1. 执行此任务（开始爬取）")
        print("  2. 删除此任务")
        print("  3. 查看所有任务")
        print("  4. 清空所有任务")
        print("  5. 跳过此任务（继续监听）")
        print("-"*60)
        
        while True:
            choice = input("请选择操作 (1-5): ").strip()
            
            if choice == '1':
                return 'execute'
            elif choice == '2':
                confirm = input(f"⚠️ 确认删除任务 {pdd_task.get('store_id', 'N/A')}? (y/n): ").strip().lower()
                if confirm in ['y', 'yes']:
                    task_list.pop(pdd_task_index)
                    self.update_redis_tasks(task_list)
                    logger.info("🗑️ 任务已删除")
                    return 'deleted'
                else:
                    logger.info("❌ 取消删除操作")
                    continue
            elif choice == '3':
                self.show_all_tasks(task_list)
                continue
            elif choice == '4':
                confirm = input("⚠️ 确认清空所有任务? (y/n): ").strip().lower()
                if confirm in ['y', 'yes']:
                    self.update_redis_tasks([])
                    logger.info("🗑️ 所有任务已清空")
                    return 'cleared'
                else:
                    logger.info("❌ 取消清空操作")
                    continue
            elif choice == '5':
                logger.info("⏭️ 跳过当前任务，继续监听")
                return 'skip'
            else:
                print("❌ 无效选择，请输入 1-5")

    def show_all_tasks(self, task_list):
        """显示所有任务"""
        print("\n" + "="*60)
        print(f"📋 所有任务列表 (共 {len(task_list)} 个)")
        print("="*60)
        
        if not task_list:
            print("📭 暂无任务")
        else:
            for i, task in enumerate(task_list, 1):
                print(f"📤 任务 {i}:")
                print(f"   🆔 Store ID: {task.get('store_id', 'N/A')}")
                print(f"   🌐 Store URL: {task.get('store_url', 'N/A')}")
                print(f"   🏷️  Platform: {task.get('platform', 'N/A')}")
                print(f"   🔄 重试次数: {task.get('retry_count', 0)}")
                print("-" * 40)
        
        input("按 Enter 键返回...")

    def start_monitoring(self, poll_interval=5):
        """开始监听Redis任务队列"""
        logger.info(f"🎧 开始监听任务队列，轮询间隔: {poll_interval}秒")
        logger.info("💡 使用 Ctrl+C 停止监听")
        
        while True:
            try:
                # 获取任务列表
                task_list = self.get_redis_tasks()
                
                if not task_list:
                    logger.debug("😴 没有任务，等待中...")
                    time.sleep(poll_interval)
                    continue
                
                logger.info(f"📝 发现 {len(task_list)} 个任务")
                
                # 找到第一个PDD任务
                pdd_task = None
                pdd_task_index = -1
                
                for i, task in enumerate(task_list):
                    if task.get("platform", "").strip() == "拼多多":
                        pdd_task = task
                        pdd_task_index = i
                        break
                
                if pdd_task is None:
                    logger.debug("📭 暂无PDD任务")
                    time.sleep(poll_interval)
                    continue
                
                # 显示任务管理菜单
                action = self.show_task_management_menu(task_list, pdd_task, pdd_task_index)
                
                if action == 'execute':
                    logger.info(f"📤 开始执行PDD任务: {pdd_task}")
                    # 处理任务（先不删除）
                    success = self.process_pdd_task(pdd_task)
                elif action in ['deleted', 'cleared']:
                    # 任务已被删除，继续监听
                    time.sleep(1)
                    continue
                elif action == 'skip':
                    # 跳过当前任务，等待一段时间后继续
                    time.sleep(poll_interval)
                    continue
                else:
                    # 未知操作，继续监听
                    continue
                
                if success:
                    # 只有成功时才从Redis删除任务
                    task_list.pop(pdd_task_index)
                    self.update_redis_tasks(task_list)
                    logger.info("✅ PDD任务执行成功，已从队列删除")
                else:
                    # 失败时更新重试次数
                    retry_count = pdd_task.get("retry_count", 0) + 1
                    task_list[pdd_task_index]["retry_count"] = retry_count
                    
                    max_retries = 3
                    if retry_count >= max_retries:
                        # 超过重试次数，删除任务
                        task_list.pop(pdd_task_index)
                        logger.error(f"❌ 任务重试次数已达上限 ({max_retries})，从队列中删除")
                    else:
                        # 未超过重试次数，保留任务并更新重试计数
                        logger.error(f"❌ PDD任务执行失败，重试次数: {retry_count}/{max_retries}")
                        logger.info("⏰ 任务保留在队列中，等待下次重试...")
                    
                    # 更新Redis
                    self.update_redis_tasks(task_list)
                    time.sleep(poll_interval * 2)  # 失败后等待更长时间再重试
                
                # 短暂休息后继续
                time.sleep(1)
                
            except KeyboardInterrupt:
                logger.info("🛑 收到中断信号，停止监听")
                break
            except Exception as e:
                logger.error(f"❌ 监听循环异常: {e}")
                time.sleep(poll_interval)
    
    def test_connection(self):
        """测试连接"""
        try:
            # 测试Redis连接
            self.redis_client.ping()
            logger.info("✅ Redis连接正常")
            
            # 检查是否有环境变量凭证
            username = os.getenv('PDD_USERNAME')
            password = os.getenv('PDD_PASSWORD')
            
            if username and password:
                logger.info("✅ PDD环境变量凭证已设置")
            else:
                logger.info("💡 PDD凭证将在处理任务时提示输入")
            
            return True
            
        except Exception as e:
            logger.error(f"❌ 连接测试失败: {e}")
            return False


def main():
    """主函数"""
    print("🚀 PDD Redis监听器")
    print("=" * 30)
    
    monitor = PDDRedisMonitor()
    
    # 测试连接
    print("🔍 测试连接...")
    if not monitor.test_connection():
        print("❌ 连接测试失败，请检查配置")
        return
    
    print("✅ 连接测试成功")
    print("🎧 启动监听器...")
    
    try:
        monitor.start_monitoring(poll_interval=5)
    except KeyboardInterrupt:
        print("\n🛑 程序已停止")
    except Exception as e:
        print(f"❌ 程序异常: {e}")

if __name__ == "__main__":
    main() 